home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume26 / em / part01 next >
Encoding:
Text File  |  1991-11-25  |  14.2 KB  |  726 lines

  1. Newsgroups: comp.sources.misc
  2. From: steve@caticsuf.CSUFresno.EDU (Steve Mitchell)
  3. Subject:  v26i074:  em - expire mail messages, Part01/01
  4. Message-ID: <1991Nov26.043002.3198@sparky.imd.sterling.com>
  5. X-Md4-Signature: 409399ad06396bdf23520a37e6c19558
  6. Date: Tue, 26 Nov 1991 04:30:02 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: steve@csufresno.edu (Steve Mitchell)
  10. Posting-number: Volume 26, Issue 74
  11. Archive-name: em/part01
  12. Environment: UNIX
  13.  
  14. I recently needed to deal with users whose mailboxes are
  15. used as depositories of information that really should be
  16. saved in separate files.  Some of the messages in mailboxes
  17. on our system were over 1000 days old!  I didn't like
  18. the utility 'junkmail', so I wrote my own.
  19.  
  20. Expire Mail works by scanning the mailboxes specified on the
  21. command line and reporting on messages that are older than the
  22. specified age, or deleting messages older than the specified
  23. age, or both.
  24.  
  25. Expire Mail has been tested under SunOS 4.1 and BSD 4.3.
  26. It also compiles and appears to work under AT&T System V 3.1.
  27.  
  28. Critiques, suggestions, questions, flames welcome.
  29.  
  30. Steve Mitchell
  31. California State University, Fresno
  32. Steve_Mitchell@csufresno.edu
  33.  ---
  34. |#| Steve Mitchell                                             KD6BET |#|
  35. |#| California Agricultural Technology Institute      +1 209 278 5675 |#|
  36. |#| California State University, Fresno           steve@csufresno.edu |#|
  37. |#| Fresno, CA  93740-0115                     "Now, Steve is happy." |#|
  38.  ---
  39. #! /bin/sh
  40. # This is a shell archive, meaning:
  41. # 1. Remove everything above the #! /bin/sh line.
  42. # 2. Save the resulting text in a file.
  43. # 3. Execute the file with /bin/sh (not csh) to create the files:
  44. #    README
  45. #    Makefile
  46. #    em.1
  47. #    em.c
  48. # This archive created: Mon Nov 25 09:33:06 1991
  49. export PATH; PATH=/bin:$PATH
  50. if test -f 'README'
  51. then
  52.     echo shar: will not over-write existing file "'README'"
  53. else
  54. cat << \SHAR_EOF > 'README'
  55.  
  56. I recently needed to deal with users whose mailboxes are
  57. used as depositories of information that really should be
  58. saved in separate files.  Some of the messages in mailboxes
  59. on our system were over 1000 days old!  I didn't like
  60. the utility 'junkmail', so I wrote my own.
  61.  
  62. Expire Mail works by scanning the mailboxes specified on the
  63. command line and reporting on messages that are older than the
  64. specified age, or deleting messages older than the specified
  65. age, or both.
  66.  
  67. Expire Mail has been tested under SunOS 4.1 and BSD 4.3.
  68. It also compiles and appears to work under AT&T System V 3.1.
  69.  
  70. Critiques, suggestions, questions, flames welcome.
  71.  
  72. Steve Mitchell
  73. California State University, Fresno
  74. Steve_Mitchell@csufresno.edu
  75. SHAR_EOF
  76. fi # end of overwriting check
  77. if test -f 'Makefile'
  78. then
  79.     echo shar: will not over-write existing file "'Makefile'"
  80. else
  81. cat << \SHAR_EOF > 'Makefile'
  82. # Good for System V
  83. #CFLAGS= -O -DUTIME 
  84.  
  85. # Good for BSD
  86. CFLAGS=    -O
  87.  
  88. em:    em.c
  89.     cc ${CFLAGS} -o em em.c
  90.  
  91. install: em em.1
  92.     install em /usr/local/bin
  93.     install em.1 /usr/local/man/man1
  94.  
  95. shar:    README Makefile em.1 em.c
  96.     shar README Makefile em.1 em.c > em.shar
  97.  
  98. SHAR_EOF
  99. fi # end of overwriting check
  100. if test -f 'em.1'
  101. then
  102.     echo shar: will not over-write existing file "'em.1'"
  103. else
  104. cat << \SHAR_EOF > 'em.1'
  105. .TH EM L "18 November 1991"
  106. .SH NAME
  107. em \- expire mail messages
  108. .SH SYNOPSIS
  109. .B em
  110. [
  111. .B \-dovz
  112. ] [
  113. .B -a
  114. .I age
  115. ] [
  116. .B -u
  117. .I username
  118. ]
  119. .I mailbox
  120. \&.\|.\|.
  121. .SH DESCRIPTION
  122. .LP
  123. .B em
  124. scans mailboxes for expired messages (messages older than
  125. .I age
  126. days) and deletes them.  Messages newer than the expiration age
  127. are left in the mailbox.
  128. .SH OPTIONS
  129. .TP
  130. .B \-a age
  131. specify how many days old an expired message is.  Default is 180
  132. (six months).
  133. .TP
  134. .B \-d
  135. Debug.  Only generate an audit trail, no actual message deletion
  136. takes place.
  137. .TP
  138. .B \-o
  139. Do not remove a mailbox just because the file's modification time
  140. is older than
  141. .I age
  142. days.  Instead, explicitly scan the mailbox to ensure that all
  143. messages have expired.
  144. .TP
  145. .B \-u username
  146. Specify that only messages from
  147. .I username
  148. will be considered for expiration.  Useful for removing all messages
  149. sent from an offensive user.
  150. .TP
  151. .B \-v
  152. Be verbose.  In addition to normal message expiration, debug information
  153. is printed as well.
  154. .TP
  155. .B \-z
  156. Do not remove mailboxes of zero length.  The default is for 
  157. .B em
  158. to remove all mailbox files that are empty.
  159. .SH EXAMPLES
  160. To remove all messages older than 60 days from all mailboxes:
  161. .LP
  162. .RS
  163. .nf
  164. .ft B
  165. example% em -a 60 /usr/spool/mail/*
  166. .ft R
  167. .fi
  168. .RE
  169. .br
  170. .ne 10
  171. .LP
  172. .SH AUTHOR
  173. Steve Mitchell (steve_mitchell@csufresno.edu)
  174. .SH WARNINGS
  175. .LP
  176. Always try
  177. .B em
  178. using the 'd' option before letting it loose on your
  179. system's mailboxes.
  180. .SH BUGS
  181. mailbox locking could be done better.
  182. SHAR_EOF
  183. fi # end of overwriting check
  184. if test -f 'em.c'
  185. then
  186.     echo shar: will not over-write existing file "'em.c'"
  187. else
  188. cat << \SHAR_EOF > 'em.c'
  189. /*
  190. ** em: expire mail messages
  191. **
  192. **
  193. ** Options:
  194. **
  195. ** -a age
  196. **       Specify how many days old an 'expired' message is.  Default
  197. **       is 180 (six months).
  198. ** -d
  199. **       Debug.  Only generates an audit trail.  No actual removal takes
  200. **       place.  More 'd's, more debugging information.
  201. ** -o
  202. **       Do not remove mailboxes just because they're older than 'age'
  203. **       days. Instead, explicitly scan the mailbox to ensure that all
  204. **       messages have expired.  The default is for em to remove any
  205. **       mailboxes that have modification times older than 'age' days.
  206. ** -u username
  207. **       Secify that only messages from 'username' will be considered
  208. **       for expiration.  Useful for removing messages from an offensive
  209. **       user.
  210. ** -v 
  211. **       Be verbose.  This option produces the same output as the debug
  212. **       option but all actions are followed through.  That is, messages
  213. **       are expired, mailboxes are removed, etc.
  214. ** -z
  215. **       Do not remove mailboxes of zero length.
  216. **
  217. **
  218. ** Author:
  219. **
  220. ** Steve Mitchell
  221. ** California State University, Fresno
  222. ** steve_mitchell@csufresno.edu
  223. **
  224. **
  225. ** NOTICE:
  226. ** 
  227. ** THE AUTHOR OFFERS NO WARRANTY FOR THE PERFORMANCE OF THIS SOFTWARE.
  228. ** (In other words, use at your own risk)
  229. **
  230. **
  231. ** Version History:
  232. **
  233. ** 09/12/91: first version
  234. **
  235. */
  236.  
  237. #include <sys/types.h>
  238. #include <sys/stat.h>
  239. #include <ctype.h>
  240. #include <sys/time.h>
  241. #include <stdio.h>
  242. #include <fcntl.h>
  243. #ifdef UTIME
  244. #ifdef sun
  245. #include <utime.h>
  246. #endif
  247. #endif
  248.  
  249. #ifndef S_ISREG
  250. #define    S_ISREG(m)    (((m)&S_IFMT) == S_IFREG)
  251. #endif
  252.  
  253. int    aged    = 160;
  254. int    sec_aged;
  255. int    debug    = 0;
  256. int    verbose = 0;
  257. int    oldflag = 1;
  258. int    zeroflag = 1;
  259. char    *fromwho = (char *)0;
  260. char    *myname;
  261. char    *whoami;
  262. time_t    currtime;
  263. #ifdef UTIME
  264. struct    utimbuf tv;
  265. #else
  266. struct    timeval    tv[2];
  267. #endif
  268. int    mbox_id;
  269.  
  270. void    usage();
  271. time_t    time();
  272. char    *getlogin();
  273. char    *strrchr();
  274.  
  275. time_t    mystrptime();
  276. void    resettimes();
  277.  
  278. struct    msg {
  279.     char    *from;
  280.     time_t    time;
  281. };
  282.  
  283. main(argc,argv)
  284.     int    argc;
  285.     char    *argv[];
  286. {
  287.     FILE    *mfp;
  288.     FILE    *ofp;
  289.     char    *mailbox;
  290.     int    c;
  291.     int    errflg = 0;
  292.     int    num_expired = 0;
  293.     extern    char    *optarg;
  294.     extern    int    optind;
  295.     struct    stat    sbuf;
  296.     char    tname[BUFSIZ];
  297.     char    *p;
  298.  
  299.     myname = argv[0];
  300.  
  301.     whoami = getlogin();
  302.  
  303.     while((c = getopt(argc,argv,"a:dou:vz")) != -1)
  304.         switch(c) {
  305.         case 'a':
  306.             aged = atoi(optarg);
  307.             break;
  308.         case 'd':
  309.             debug++;
  310.             break;
  311.         case 'o':
  312.             oldflag = 0;
  313.             break;
  314.         case 'u':
  315.             fromwho = optarg;
  316.             break;
  317.         case 'v':
  318.             verbose = 1;
  319.             break;
  320.         case 'z':
  321.             zeroflag = 0;
  322.             break;
  323.         default:
  324.             errflg++;
  325.         }
  326.  
  327.     if(errflg || optind == argc) {
  328.         usage();
  329.         return(1);
  330.     }
  331.  
  332.     sec_aged = aged * 86400;
  333.  
  334.     (void)time(&currtime);
  335.  
  336.     for(;optind < argc; optind++) {
  337.         mailbox = argv[optind];
  338.  
  339.         if(stat(mailbox,&sbuf) < 0) {
  340.             perror(mailbox);
  341.             continue;
  342.         } 
  343. #ifdef UTIME
  344.         tv.actime = sbuf.st_atime;
  345.         tv.modtime = sbuf.st_mtime;
  346. #else
  347.         tv[0].tv_sec = sbuf.st_atime;
  348.         tv[1].tv_sec = sbuf.st_mtime;
  349. #endif
  350.  
  351.         mbox_id = sbuf.st_uid;
  352.  
  353.         p = strrchr(mailbox,'.');
  354.         if(p)
  355.             if(!strcmp(p,".lock"))
  356.                 continue;
  357.  
  358.         if(locked(mailbox)) {
  359.             (void)fprintf(stderr,"%s: locked\n",mailbox);
  360.             continue;
  361.         }
  362. #ifdef    S_ISREG
  363.         if(!S_ISREG(sbuf.st_mode)) {
  364.             (void)fprintf(stderr,"%s: not a regular file\n",
  365.                     mailbox);
  366.             continue;
  367.         }
  368. #endif
  369.         if(debug >= 1 || verbose >= 1)
  370.             (void)printf("%s:\n",mailbox);
  371.         if(currtime - sbuf.st_mtime > sec_aged && oldflag) {
  372.             if(debug >= 1 || verbose >= 1)
  373.                 (void)printf(
  374.                     "\tolder than %d days - remove\n",aged);
  375.             if(!debug)
  376.                 if(unlink(mailbox) < 0)
  377.                     perror(mailbox);
  378.             continue;
  379.         }
  380.         if(sbuf.st_size == 0 && zeroflag) {
  381.             if(debug >= 1 || verbose >= 1)
  382.                 (void)printf("\tzero length - remove\n");
  383.             if(!debug)
  384.                 if(unlink(mailbox) < 0)
  385.                     perror(mailbox);
  386.             continue;
  387.         }
  388.         if(!lock(mailbox)) {
  389.             (void)fprintf(stderr,"%s: can't lock\n",mailbox);
  390.             continue;
  391.         }
  392.         if((mfp = fopen(mailbox,"r")) == NULL) {
  393.             perror(mailbox);
  394.             continue;
  395.         }
  396.  
  397.         p = strrchr(mailbox,'/');
  398.         if(p)
  399.             p++;
  400.         else
  401.             p = mailbox;
  402.         (void)sprintf(tname,"/tmp/%s.expire",p);
  403.  
  404.         if((ofp = fopen(tname,"w")) == NULL) {
  405.             perror(tname);
  406.             (void)fclose(mfp);
  407.             resettimes(mailbox);
  408.             (void)unlock(mailbox);
  409.             continue;
  410.         }
  411.  
  412.         num_expired = do_msgs(mfp,ofp);
  413.  
  414.         if(stat(tname,&sbuf) < 0) {
  415.             perror(tname);
  416.             (void)unlock(mailbox);
  417.             continue;
  418.         } 
  419.         if(sbuf.st_size == 0 && zeroflag) {
  420.             if(debug >= 1 || verbose >= 1) {
  421.                 (void)printf("\tzero length - remove\n");
  422.                 resettimes(mailbox);
  423.             } 
  424.             if(!debug)
  425.                 if(unlink(mailbox) < 0)
  426.                     perror(mailbox);
  427.             if(unlink(tname) < 0)
  428.                 perror(tname);
  429.             (void)unlock(mailbox);
  430.             continue;
  431.         }
  432.         if(debug || !num_expired) {
  433.             (void)unlink(tname);
  434.             (void)fclose(mfp);
  435.             resettimes(mailbox);
  436.             if(!unlock(mailbox)) 
  437.                 (void)fprintf(stderr,
  438.                     "%s: can't unlock\n",mailbox);
  439.             continue;
  440.         }
  441.  
  442.         if((mfp = fopen(mailbox,"w")) == NULL) {
  443.             perror(mailbox);
  444.             (void)unlock(mailbox);
  445.             continue;
  446.         }
  447.         if((ofp = fopen(tname,"r")) == NULL) {
  448.             perror(tname);
  449.             (void)fclose(mfp);
  450.             resettimes(mailbox);
  451.             (void)unlock(mailbox);
  452.             continue;
  453.         }
  454.         while((c = fgetc(ofp)) != EOF)
  455.             (void)fputc(c,mfp);
  456.         (void)fclose(ofp);
  457.         (void)fclose(mfp);
  458.  
  459.         resettimes(mailbox);
  460.  
  461.         if(!unlock(mailbox)) {
  462.             (void)fprintf(stderr,"%s: can't unlock\n",mailbox);
  463.             continue;
  464.         }
  465.             
  466.         if(unlink(tname) < 0)
  467.             perror(tname);
  468.     }
  469.     return(0);
  470. }
  471.  
  472.  
  473. void
  474. resettimes(mbox)
  475.     char    *mbox;
  476. {
  477.     if(geteuid() == 0 || getuid() == mbox_id ) {
  478. #ifdef UTIME
  479.         if(utime(mbox,&tv) < 0) {
  480.             perror("utime");
  481.         }
  482. #else
  483.         tv[0].tv_usec = (time_t)0;
  484.         tv[1].tv_usec = (time_t)0;
  485.         if(utimes(mbox,tv) < 0) {
  486.             perror("utimes");
  487.         }
  488. #endif
  489.     }
  490. }
  491.  
  492. do_msgs(mfp,ofp)
  493.     FILE    *mfp;
  494.     FILE    *ofp;
  495. {
  496.     int    writeout = 1;
  497.     int    msgnum = 0;
  498.     char    buf[BUFSIZ];
  499.     struct    msg msg;
  500.     int    num_expired = 0;
  501.  
  502.     
  503.     while(fgets(buf,BUFSIZ,mfp)) {
  504.         if(!strncmp("From ",buf,5)) {
  505.             msgnum++;
  506.             get_msg_info(buf,&msg);
  507.             if(msg.time < 0) {
  508.                 (void)fprintf(stderr,"Bad header\n");
  509.                 continue;
  510.             }
  511.             if(debug >= 3 || verbose >= 3)
  512.                 (void)printf("\t\tmessage from %s %d\n",
  513.                         msg.from, msg.time);
  514.             if(currtime - msg.time > sec_aged) {
  515.                 writeout = 0;
  516.                 num_expired++;
  517.                 if(fromwho && strcmp(fromwho,msg.from)) 
  518.                     writeout = 1;
  519.                 if((debug >= 1 || verbose >= 1) && !writeout)
  520.                          (void)printf(
  521.                          "\tmessage #%d, expired (%d days)\n",
  522.                          msgnum,(currtime-msg.time)/86400);
  523.                 if(debug >= 2 || verbose >= 2)
  524.                     (void)printf(
  525.                      "\t\tcurrtime = %d, msg.time = %d\n",
  526.                     currtime,msg.time);
  527.                 if(debug >= 2 || verbose >= 2)
  528.                          (void)printf("\t\tfrom = %s\n", msg.from);
  529.             } else {
  530.                 writeout = 1;
  531.             }
  532.         }
  533.         if(writeout)
  534.             (void)fprintf(ofp,"%s",buf);
  535.     }
  536.     (void)fclose(mfp);
  537.     (void)fclose(ofp);
  538.     return(num_expired);
  539. }
  540.  
  541.  
  542. get_msg_info(fromp,s)
  543.     char    *fromp;
  544.     struct msg *s;
  545. {
  546.     static    char    name[1024];
  547.     char    *p, *wp;
  548. #ifdef HAVE_STRPTIME
  549.     struct    tm tm;
  550. #endif
  551.  
  552.     for(p=fromp;*p!=' ';p++)
  553.         ;
  554.     for(;*p==' ';p++)
  555.         ;
  556.     for(wp=name;*p!=' ';p++,wp++)
  557.         *wp = *p;
  558.     *wp = 0;
  559.     s->from = name;
  560.  
  561.  
  562.     for(;*p==' ';p++)
  563.         ;
  564.  
  565. #ifdef HAVE_STRPTIME
  566.     strptime(p,"%a %h %e %H:%M:%S %Y",&tm);
  567.     s->time = timelocal(&tm);
  568. #else
  569.     s->time = mystrptime(p);
  570. #endif /* HAVE_STRPTIME */
  571.     if(debug >= 3) {
  572.         (void)printf("\t\ttimelocal = %ld\n",s->time);
  573.     }
  574. }
  575.  
  576. #define    equal(S1,S2)    (!strcmp(S1,S2))
  577.  
  578. #define MINUTE    (60L)
  579. #define HOUR    (MINUTE * 60L)
  580. #define DAY    (HOUR    * 24L)
  581. #define RYEAR    (DAY    * 365L)
  582. #define LYEAR    (DAY    * 366L)
  583.  
  584. struct mons {
  585.     int    days;
  586.     char    *month;
  587. } months[] = {
  588.     31,    "Jan",
  589.     28,    "Feb",
  590.     31,    "Mar",
  591.     30,    "Apr",
  592.     31,    "May",
  593.     30,    "Jun",
  594.     31,    "Jul",
  595.     31,    "Aug",
  596.     30,    "Sep",
  597.     31,    "Oct",
  598.     30,    "Nov",
  599.     31,    "Dec",
  600.      0,    0
  601. };
  602.  
  603. time_t
  604. mystrptime(s)
  605.     char    *s;
  606. {
  607.     struct    mons    *mp;
  608.     char    mymon[4];
  609.     char    myyear[5];
  610.     char    *p;
  611.     int    dom, tot=0, year, mfound=0;
  612.     int    hr, min;
  613.     int    dindex = 70;
  614.     long    sectot=0;
  615.  
  616.     if(sscanf(s,"%*s %s %d %d:%d",
  617.                 mymon,&dom,&hr,&min) < 3) {
  618.         (void)fprintf(stderr,"Bad date format\n");
  619.         return(-1);
  620.     }
  621.  
  622.     p = s + strlen(s);
  623.     for(;!isdigit(*p);p--)
  624.         ;
  625.     p-=3;
  626.     (void)strncpy(myyear,p,4);
  627.     myyear[4] = 0;
  628.  
  629.     year = atoi(myyear);
  630.     
  631.     /* handle leap years */
  632.     if((year % 4) == 0)
  633.         months[1].days++;
  634.  
  635.     /* add to tot until we find the month we're looking for */
  636.     for(mp=months;mp->days;mp++) {
  637.         if(equal(mp->month,mymon)) {
  638.             mfound++;
  639.             break;
  640.         }
  641.         tot += mp->days;
  642.     }
  643.  
  644.     if(!mfound) {
  645.         (void)fprintf(stderr,"Bad date format\n");
  646.         return(-1);
  647.     }
  648.  
  649.     sectot = ((tot+dom-1)*DAY) + (hr*HOUR) + (min*MINUTE);
  650.  
  651.     for(dindex=70;dindex<(year-1900);dindex++)
  652.         if(dindex % 4 == 0)
  653.             sectot += LYEAR;
  654.         else
  655.             sectot += RYEAR;
  656.  
  657.     return(sectot);
  658. }
  659.  
  660. locked(file)
  661.     char    *file;
  662. {
  663.     char    lfile[BUFSIZ];
  664.     struct    stat sbuf;
  665.     int    count = 0;
  666.  
  667.     (void)sprintf(lfile,"%s.lock",file);
  668.  
  669.     for(count = 0; count < 3; count++) {
  670.         if(stat(lfile,&sbuf) < 0)
  671.             break;
  672.         else
  673.             sleep(3);
  674.     }
  675.     return(count==3);
  676. }
  677.  
  678. lock(file)
  679.     char    *file;
  680. {
  681.     char    fname[BUFSIZ];
  682.     int    fd;
  683.  
  684.     (void)sprintf(fname,"%s.lock",file);
  685.  
  686.     if((fd = open(fname,O_CREAT,0700)) < 0) {
  687.         perror(fname);
  688.         return(0);
  689.     }
  690.     (void)close(fd);
  691.     return(1);
  692. }
  693.  
  694. unlock(file)
  695.     char    *file;
  696. {
  697.     char    fname[BUFSIZ];
  698.  
  699.     (void)sprintf(fname,"%s.lock",file);
  700.  
  701.     if(unlink(fname) < 0) {
  702.         perror(fname);
  703.         return(0);
  704.     }
  705.     return(1);
  706. }
  707.     
  708. void
  709. usage()
  710. {
  711.     (void)fprintf(stderr,
  712.     "Usage: %s [-a age] [-d] [-o] [-u username] [-v] [-z] mailbox..\n",
  713.         myname);
  714. }
  715. SHAR_EOF
  716. fi # end of overwriting check
  717. #    End of shell archive
  718. exit 0
  719.  
  720. exit 0 # Just in case...
  721. -- 
  722. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  723. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  724. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  725. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  726.